Skip to content

fix: detect generated contract drift without rewriting outputs - #1434

Merged
kang-heewon merged 9 commits into
trunkfrom
fix/1322-codegen-drift
Jul 24, 2026
Merged

fix: detect generated contract drift without rewriting outputs#1434
kang-heewon merged 9 commits into
trunkfrom
fix/1322-codegen-drift

Conversation

@kang-heewon

@kang-heewon kang-heewon commented Jul 21, 2026

Copy link
Copy Markdown
Member

Outcome

OpenAPI and RPC code generation now provide an explicit read-only --output-check mode that compares committed artifacts with the production emitters without changing files. Drift reports stable missing, changed, or unexpected-file diagnostics and an exact recovery command, while the existing graph-diagnostic --check contract remains unchanged.

Generated admin-console, SaaS, AI-SaaS, and split-app projects now separate intentional codegen writes from read-only contract:verify checks. Generated-app smoke coverage guards tracked files so CI proves contracts were already synchronized.

Verification

  • @croco/openapi-spec suite — 59/59 tests
  • @croco/rpc-codegen suite — 112/112 tests
  • create-croco-app suite — 108/108 tests
  • generated-app target smoke — admin console, SaaS, AI-SaaS, and REST SPA passed with no tracked-file mutation
  • verification policy and generated-smoke suites — 43/43 tests
  • pre-push repository tests — 226/226 tasks
  • pre-push repository typecheck — 225/225 tasks
  • changeset-required gate — passed
  • lint, formatting, contract, architecture, security, Problem registry, and public API gates — passed

Review gates

  • Correctness/regression: PASS — changed and missing OpenAPI output plus changed, missing, and unexpected RPC files fail; unchanged outputs preserve bytes, mtimes, and directory contents.
  • API/security/compatibility/release: PASS — --check is unchanged; the new mode is additive; package manifests, dependencies, lockfile, and public exports are unchanged; patch changesets cover all affected packages.
  • Maintainability/minimality: PASS — write and check paths share one in-memory emitter per package, and generated verification is read-only without a second generator implementation.
  • Independent adversarial review: APPROVE, 0 findings after re-review.

Validation note

The complete generated-app smoke run passed every issue-scoped case; the unrelated graphql-vite-spa-astryx case on trunk still fails during SSR because its packaged UI references a missing React global. This change does not touch that package or path.

Fixes #1322

Summary by CodeRabbit

  • 새로운 기능
    • --output-check로 OpenAPI 문서 및 RPC 클라이언트 생성 결과의 드리프트를 덮어쓰기 없이 검사할 수 있습니다.
    • 누락/변경이 발견되면 상태와 재생성 안내를 출력하며 실패합니다.
  • 버그 수정
    • 생성된 앱의 계약 검증을 읽기 전용 검사 방식으로 전환해, 검증 중 산출물 재생성/재작성 동작을 방지합니다.
    • 템플릿별 계약 검증·코드 생성 스크립트 실행 흐름을 일관되게 정리했습니다.
  • 문서
    • 계약 스냅샷, 코드 생성, 검증 및 산출물 관리 절차를 최신 흐름에 맞게 업데이트했습니다.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kang-heewon, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 18 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 45d45949-5ec7-42b3-aa08-e6f5e46659a8

📥 Commits

Reviewing files that changed from the base of the PR and between 769cb34 and 1f02444.

⛔ Files ignored due to path filters (1)
  • packages/problems-core/src/generated/problem-code-registry.ts is excluded by !**/generated/**
📒 Files selected for processing (30)
  • .changeset/read-only-codegen-drift.md
  • docs/problem-code-registry.json
  • packages/create-croco-app/src/tests/e2e-generation.spec.ts
  • packages/create-croco-app/src/tests/templates-build.spec.ts
  • packages/create-croco-app/templates/admin-console/README.md.hbs
  • packages/create-croco-app/templates/admin-console/package.json.hbs
  • packages/create-croco-app/templates/ai-saas/README.md.hbs
  • packages/create-croco-app/templates/ai-saas/package.json.hbs
  • packages/create-croco-app/templates/saas/README.md.hbs
  • packages/create-croco-app/templates/saas/package.json.hbs
  • packages/create-croco-app/templates/spa-be-split/README.md.hbs
  • packages/create-croco-app/templates/spa-be-split/package.json.hbs
  • packages/docs/src/content/docs/api/problems-core/src/variables/CROCO_PROBLEM_CODE_REGISTRY.md
  • packages/docs/src/content/docs/en/reference/problem-recovery-cookbook.md
  • packages/openapi-spec/src/libs/cli.ts
  • packages/openapi-spec/src/libs/output.ts
  • packages/openapi-spec/src/tests/Cli.spec.ts
  • packages/openapi-spec/src/tests/Output.spec.ts
  • packages/rpc-codegen/src/libs/cli.ts
  • packages/rpc-codegen/src/libs/generate.ts
  • packages/rpc-codegen/src/tests/Cli.spec.ts
  • packages/rpc-codegen/src/tests/codegen.spec.ts
  • scripts/alpha-release-smoke.mts
  • scripts/create-croco-app-generated-smoke.mts
  • scripts/static-misuse-raw-error-allowlist.json
  • scripts/tests/alpha-release-smoke.spec.ts
  • scripts/tests/create-croco-app-generated-smoke.spec.ts
  • scripts/tests/verification-policy.spec.ts
  • scripts/verification-policy.mts
  • tsconfig/contract-strict.baseline.json
📝 Walkthrough

Walkthrough

OpenAPI와 RPC 코드 생성 CLI에 출력 드리프트 검사 기능이 추가되었습니다. 생성 앱 템플릿은 생성과 읽기 전용 검증을 분리하고, 스모크 테스트와 검증 정책은 새로운 계약 검사 흐름을 반영합니다.

Changes

읽기 전용 코드 생성 드리프트

Layer / File(s) Summary
OpenAPI 출력 드리프트 검사
packages/openapi-spec/src/libs/*, packages/openapi-spec/src/tests/*
--output-check가 OpenAPI 산출물을 쓰지 않고 missing 또는 changed 상태를 비교하며, 직렬화·개행 정규화·CLI 테스트가 추가되었습니다.
RPC 클라이언트 출력 드리프트 검사
packages/rpc-codegen/src/libs/*, packages/rpc-codegen/src/tests/*
RPC 생성 로직과 비교 로직이 분리되고, missing·changed·unexpected 파일과 재생성 명령을 보고하도록 변경되었습니다.
생성 앱 계약 스크립트와 문서
packages/create-croco-app/templates/*, packages/create-croco-app/src/tests/*
codegen은 산출물을 생성하고 contract:verifycontract:*-check로 드리프트만 검사하도록 갱신되었습니다. README와 템플릿 검증 기대값도 변경되었습니다.
스모크 검증과 정책 분류
scripts/create-croco-app-generated-smoke.mts, scripts/verification-policy.mts, scripts/tests/*
생성 앱 스모크가 codegen 후 읽기 전용 계약 검증을 실행하며, --output-check와 새 검증 스크립트가 회귀 테스트 정책에 포함되었습니다.
템플릿 릴리스와 메타데이터
.changeset/*, packages/create-croco-app/templates/addons/*, docs/*, tsconfig/*
패치 릴리스 범위, Next.js 15.5.21 템플릿, 문제 소스와 진단 기준선의 라인 메타데이터가 갱신되었습니다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Developer
  participant CodegenCLI
  participant GeneratedOutput
  participant ContractVerify
  Developer->>CodegenCLI: pnpm codegen
  CodegenCLI->>GeneratedOutput: write OpenAPI and RPC outputs
  Developer->>ContractVerify: pnpm contract:verify
  ContractVerify->>CodegenCLI: run --output-check
  CodegenCLI->>GeneratedOutput: compare without writing
  GeneratedOutput-->>ContractVerify: drift status
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning addon web template package.json.hbs의 Next.js 15.5.21 버전 업은 #1322의 출력 드리프트 감지 요구와 직접 관련이 없습니다. 이 의존성 업그레이드는 별도 PR로 분리하거나, 해당 릴리스/호환성 요구사항과의 연결 근거를 추가하세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 출력 재작성 없이 계약 드리프트를 감지한다는 핵심 변경을 간결하게 잘 요약합니다.
Linked Issues check ✅ Passed read-only output-check, in-memory emitters, changed/missing/unexpected 감지, 복구 명령, read-only contract:verify/codegen 분리가 모두 반영됐습니다.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1322-codegen-drift

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown

📊 Benchmark Results

✅ All benchmarks passed

Benchmark p75 Threshold Baseline vs Baseline Status Notes
CrocoApp constructor 8.5μs 30.0ms 8.2μs +4.4% -
CrocoApp lambdaHandler (10 controllers) 277.7μs 50.0ms 258.4μs +7.5% -
Lambda cold-start simulation 448.5μs 80.0ms 418.1μs +7.3% -
Lambda cold-start with headers 394.5μs 80.0ms 369.7μs +6.7% -
Lambda cold-start with binary body 378.4μs 80.0ms 339.1μs +11.6% -
Lambda cold-start with query params 321.8μs 80.0ms 301.3μs +6.8% -
Lambda cold-start with authorizer context 325.9μs 80.0ms 299.8μs +8.7% -
Lambda cold-start realistic scenario 326.0μs 80.0ms 299.2μs +9.0% -
EventBusConfig.start (10 handlers) 1.5μs 10.0ms 1.4μs +7.0% -
EventPublisher.publishNow single event 1.8μs 2.0ms 1.7μs +4.2% -
DefaultHandlerResolver.resolve × 10 0.1μs 5.0ms 0.1μs -11.2% -
Container.get singleton (cold) 77.2μs 5.0ms 70.3μs +9.8% -
Container.register × 50 components 3.5ms 10.0ms 3.2ms +9.8% -
Container.validate (50 components) 4.1ms 20.0ms 3.4ms +19.8% -
Container.get singleton (warm) 1.7μs 500.0μs 1.6μs +3.7% -
TelemetryRuntime.init (lambda preset) 2.4μs 200.0ms 1.1ms -99.8% -
lambdaPreset config creation 1.5μs 2.0ms 1.4μs +3.6% -

Updated: 2026-07-23T01:58:23.853Z · Commit: 286564d

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/create-croco-app/templates/spa-be-split/package.json.hbs`:
- Around line 16-27: Remove the redundant provider-rpc typecheck invocation from
the contract:verify script. Keep contract:client:check unchanged, since it
already runs pnpm --filter {{scope}}/provider-rpc typecheck.

In `@packages/rpc-codegen/src/libs/generate.ts`:
- Around line 3469-3541: normalizeGeneratedContent와 openapi-spec의 중복된 개행 정규화 로직을
하나의 공유 유틸리티로 추출하세요. generate.ts의 normalizeGeneratedContent 구현을 제거하고
compareClientFiles가 공유 유틸리티를 사용하도록 변경하며, openapi-spec 측도 동일한 유틸리티를 재사용하게 하세요.
기존의 CRLF 및 CR을 LF로 변환하는 동작은 유지하세요.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 89a08ec1-c173-4830-9489-ab7df3e5929f

📥 Commits

Reviewing files that changed from the base of the PR and between c1ce071 and ffc3dcf.

⛔ Files ignored due to path filters (1)
  • packages/problems-core/src/generated/problem-code-registry.ts is excluded by !**/generated/**
📒 Files selected for processing (27)
  • .changeset/read-only-codegen-drift.md
  • docs/problem-code-registry.json
  • packages/create-croco-app/src/tests/e2e-generation.spec.ts
  • packages/create-croco-app/src/tests/templates-build.spec.ts
  • packages/create-croco-app/templates/admin-console/README.md.hbs
  • packages/create-croco-app/templates/admin-console/package.json.hbs
  • packages/create-croco-app/templates/ai-saas/README.md.hbs
  • packages/create-croco-app/templates/ai-saas/package.json.hbs
  • packages/create-croco-app/templates/saas/README.md.hbs
  • packages/create-croco-app/templates/saas/package.json.hbs
  • packages/create-croco-app/templates/spa-be-split/README.md.hbs
  • packages/create-croco-app/templates/spa-be-split/package.json.hbs
  • packages/docs/src/content/docs/en/reference/problem-recovery-cookbook.md
  • packages/openapi-spec/src/libs/cli.ts
  • packages/openapi-spec/src/libs/output.ts
  • packages/openapi-spec/src/tests/Cli.spec.ts
  • packages/openapi-spec/src/tests/Output.spec.ts
  • packages/rpc-codegen/src/libs/cli.ts
  • packages/rpc-codegen/src/libs/generate.ts
  • packages/rpc-codegen/src/tests/Cli.spec.ts
  • packages/rpc-codegen/src/tests/codegen.spec.ts
  • scripts/create-croco-app-generated-smoke.mts
  • scripts/static-misuse-raw-error-allowlist.json
  • scripts/tests/create-croco-app-generated-smoke.spec.ts
  • scripts/tests/verification-policy.spec.ts
  • scripts/verification-policy.mts
  • tsconfig/contract-strict.baseline.json

Comment thread packages/create-croco-app/templates/spa-be-split/package.json.hbs Outdated
Comment thread packages/rpc-codegen/src/libs/generate.ts
@kang-heewon

Copy link
Copy Markdown
Member Author

Pre-merge evidence: all issue-scoped checks pass, CodeRabbit approved the latest commit, and both review threads are resolved. The remaining validate failure is the same dependency-audit-policy failure present on current trunk, with no manifest or lockfile changes in this PR. The companion report has 0 generated-app gate failures and fails only in the unrelated graphql-vite-spa-astryx SSR case because the packaged ui-astryx build references a missing React global; every issue-scoped generated-app case passes. Local pre-push repository tests are 226/226 and typecheck is 225/225.

@kang-heewon
kang-heewon force-pushed the fix/1322-codegen-drift branch from ba0eb1c to c518f67 Compare July 21, 2026 19:46
@kang-heewon

Copy link
Copy Markdown
Member Author

Updated onto current trunk at ba1c12d. The trunk lockfile and dependency overrides resolve the prior blocking brace-expansion, js-yaml, and axios audit findings; dependency-audit-policy now passes locally. Fresh validation on head c518f67: repository profile passed 21/22 with one non-applicable check, package suites passed OpenAPI 59/59, RPC 112/112, create-croco-app 108/108, all four issue-scoped generated-app smoke cases passed, pre-push tests passed 226/226, and typecheck passed 225/225.

@kang-heewon

Copy link
Copy Markdown
Member Author

Post-fix verification for head 2c1c90f:

  • Fixed the packed release smoke sequence to run intentional codegen before the read-only contract:verify gate.
  • pnpm vitest run scripts/tests/alpha-release-smoke.spec.ts: 7/7 passed.
  • pnpm alpha-release:smoke: passed, including packed OpenAPI/RPC output drift verification.
  • pre-push repository tests: 226/226 passed.
  • pre-push repository typecheck: 225/225 passed.
  • CI publish profile: 42/43 checks passed; the issue-scoped alpha-release-smoke now passes.

The remaining required-check failure is external to #1322: dependency-audit-policy reports GHSA-45rx-2jwx-cxfr in @opentelemetry/propagator-jaeger@2.7.1 on trunk. The patched OpenTelemetry dependency set is currently carried by #1431 and has not merged yet.

The optional companion check also has only the pre-existing graphql-vite-spa-astryx failure (ReferenceError: React is not defined, 1 case failure / 0 gate failures), likewise addressed by #1431.

@kang-heewon
kang-heewon force-pushed the fix/1322-codegen-drift branch 5 times, most recently from 9f2d3cd to 780e0be Compare July 22, 2026 22:54

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/create-croco-app-generated-smoke.mts (1)

2373-2396: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

검증 실패를 Problem 하위 타입으로 표준화하세요.

새 런타임 계약 검증이 일반 Error를 던집니다. 코드와 category를 가진 전용 Problem 하위 타입을 사용해야 합니다. As per coding guidelines, “Throw only Problem subclasses, never generic Error instances”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/create-croco-app-generated-smoke.mts` around lines 2373 - 2396,
Update the runtime contract validation checks in the smoke-test flow to throw
the established Problem subclass instead of generic Error instances. Ensure each
validation failure preserves its descriptive message and uses the required code
and category fields, including the checks for engines.node, .nvmrc, README
guidance, and deployment-runtime wording.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/rpc-codegen/src/libs/generate.ts`:
- Line 3524: Update the diagnostic sorting in the return path associated with
compareClientFiles() to use deterministic code-unit string comparison instead of
localeCompare(). Preserve sorting by each drift’s filePath so diagnostics and
regeneration commands have stable ordering across locales.

---

Outside diff comments:
In `@scripts/create-croco-app-generated-smoke.mts`:
- Around line 2373-2396: Update the runtime contract validation checks in the
smoke-test flow to throw the established Problem subclass instead of generic
Error instances. Ensure each validation failure preserves its descriptive
message and uses the required code and category fields, including the checks for
engines.node, .nvmrc, README guidance, and deployment-runtime wording.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 319b503e-6c92-4bed-9943-15e0e7092126

📥 Commits

Reviewing files that changed from the base of the PR and between ffc3dcf and 769cb34.

⛔ Files ignored due to path filters (1)
  • packages/problems-core/src/generated/problem-code-registry.ts is excluded by !**/generated/**
📒 Files selected for processing (34)
  • .changeset/read-only-codegen-drift.md
  • docs/problem-code-registry.json
  • packages/create-croco-app/src/tests/e2e-generation.spec.ts
  • packages/create-croco-app/src/tests/templates-build.spec.ts
  • packages/create-croco-app/templates/addons/graphql-nextjs/apps/web/package.json.hbs
  • packages/create-croco-app/templates/addons/trpc-nextjs/apps/web/package.json.hbs
  • packages/create-croco-app/templates/addons/web-graphql/apps/web/package.json.hbs
  • packages/create-croco-app/templates/addons/web-trpc/apps/web/package.json.hbs
  • packages/create-croco-app/templates/admin-console/README.md.hbs
  • packages/create-croco-app/templates/admin-console/package.json.hbs
  • packages/create-croco-app/templates/ai-saas/README.md.hbs
  • packages/create-croco-app/templates/ai-saas/package.json.hbs
  • packages/create-croco-app/templates/saas/README.md.hbs
  • packages/create-croco-app/templates/saas/package.json.hbs
  • packages/create-croco-app/templates/spa-be-split/README.md.hbs
  • packages/create-croco-app/templates/spa-be-split/package.json.hbs
  • packages/docs/src/content/docs/api/problems-core/src/variables/CROCO_PROBLEM_CODE_REGISTRY.md
  • packages/docs/src/content/docs/en/reference/problem-recovery-cookbook.md
  • packages/openapi-spec/src/libs/cli.ts
  • packages/openapi-spec/src/libs/output.ts
  • packages/openapi-spec/src/tests/Cli.spec.ts
  • packages/openapi-spec/src/tests/Output.spec.ts
  • packages/rpc-codegen/src/libs/cli.ts
  • packages/rpc-codegen/src/libs/generate.ts
  • packages/rpc-codegen/src/tests/Cli.spec.ts
  • packages/rpc-codegen/src/tests/codegen.spec.ts
  • scripts/alpha-release-smoke.mts
  • scripts/create-croco-app-generated-smoke.mts
  • scripts/static-misuse-raw-error-allowlist.json
  • scripts/tests/alpha-release-smoke.spec.ts
  • scripts/tests/create-croco-app-generated-smoke.spec.ts
  • scripts/tests/verification-policy.spec.ts
  • scripts/verification-policy.mts
  • tsconfig/contract-strict.baseline.json

Comment thread packages/rpc-codegen/src/libs/generate.ts Outdated
@kang-heewon
kang-heewon force-pushed the fix/1322-codegen-drift branch from 769cb34 to 1f02444 Compare July 23, 2026 01:49
@kang-heewon
kang-heewon merged commit 72fbcd0 into trunk Jul 24, 2026
10 checks passed
@kang-heewon
kang-heewon deleted the fix/1322-codegen-drift branch July 24, 2026 05:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[codegen] Detect committed OpenAPI and RPC drift without rewriting outputs

1 participant